/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * Licensed under Simplified BSD License
 *
 * This is a temp solution, should use DfMatrixCanvas
 * @author: jin Chen 
 * @date: Nov 21, 2005$
 * @version: 1.0
 */
package edu.psu.geovista.app.gmatrix.component;

import edu.psu.geovista.app.gmatrix.control.SelectClient;
import edu.psu.geovista.app.gmatrix.toolkit.FireEventSupport;
import edu.psu.geovista.app.gmatrix.toolkit.MatrixLoggerMgr;
import epoviz.trex.common.swing.frame.tool.BasicMouseTool;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

public class DfMatrixHeader extends JPanel implements MatrixHeader{
   private static final Logger logger= Logger.getLogger(DfMatrixHeader.class.getName());
    //size &layout
    private boolean selfcomputesize=true;//by default true: computer cell centers by itself. When called setCellCetners(), it become false
    private double[] cellcenters;// 
    private Point[] locs=new Point[0];// one dimension of location. The other dimension =0
    private Dimension cellsize=new Dimension();

    private int orient=SwingConstants.HORIZONTAL;//SwingConstants.HORIZONTAL: columnHeader;  SwingConstants.VERTICAL : rowHeader
    private int indsize;// independent size: one dimension ( w/h) is decide by user

    //GUI
    private Color selectedColor=new Color(192,192,255); //color for selected cells, DON"T use semi-transparent since with dendrogram, default head label will be painted under
    //other properties
    private String[] celllabels;

    //tools
    JLabel cellrender;   //render label
    CellRendererPane renderpane;//=new CellRendererPane();


    //selecting related
    Set <Integer> selection =new HashSet<Integer>();
    private FireEventSupport evtfirer;
    private MouseListener selectSupport;
    private SelectClient selectclient;
    /**
     *
     * @param indsize     independent size
     * @param orient
     */
    public DfMatrixHeader(int indsize,int orient) {
        this.indsize = indsize;
        this.orient =orient;
        cellrender=getDefaultCellRender();
        renderpane=new CellRendererPane();

        evtfirer=new FireEventSupport(this);
        selectSupport=getSelectSupport();
        this.addMouseListener(selectSupport);
    }



    public void paintComponent(Graphics g){
        Rectangle bd=new Rectangle();
        int numOfCell = getNumOfCell();
        for (int i=0;i<locs.length  ;i++){ //locs.length todo replace locs.length with getNumOfCell()?
         JLabel cellrender=   getCellrender(i, bd);
            //cellrender.setBorder(BorderFactory.createLineBorder(Color.green) );

             /*  if(logger.isLoggable(Level.FINE )){
                    System.out.println(DfMatrixHeader.class.getName()+" paint cell header:"+bd);
                }*/
                renderpane.paintComponent(g,cellrender,this,bd.x, bd.y,bd.width ,bd.height );

        }

    }

    /**
     * get cell render for paint ith cell
     * @param i     index of cell
     * @param bd    any  Rectangle object, if null will create one
     */
    protected JLabel getCellrender(int i, Rectangle bd) {
        if(bd==null)bd=new Rectangle();
        JLabel cellrender = this.getCellrender();
        String label = getLabel(i);
        if(label!=null){
            cellrender.setText(label);
        }
        else{
            cellrender.setText(null);
        }
        getCellBound(i,bd);
        cellrender.setBounds(bd);
        cellrender.validate();
        //cellrender.setForeground(Color.black);
        if(selection.contains(i)){
            if(this.isColumnHeader() ){
                MatrixLoggerMgr.fine(logger,"     select column: "+i);
            }
            else{
                MatrixLoggerMgr.fine(logger,"     select row: "+i);
            }
            cellrender.setBackground(selectedColor);
            cellrender.setOpaque(true);
        }
        else{
            cellrender.setBackground(Color.lightGray);
             cellrender.setOpaque(true);
        }
        return cellrender;
    }

    public JLabel getDefaultCellRender(){
        if(this.cellrender==null){
           JLabel cellrender=new JLabel();
           cellrender.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY ) );
           cellrender.setHorizontalAlignment(SwingConstants.CENTER );
           cellrender.setFont(new Font("Arial",Font.BOLD ,12));
           cellrender.setHorizontalAlignment(SwingConstants.CENTER );
           this.cellrender=cellrender;
        }
       return cellrender;
   }

    protected String getLabel(int index){
        if(celllabels!=null&&celllabels.length >index){
            return celllabels[index];
        }
        return null;

    }
    protected void getCellBound(int index, Rectangle r){
        Point loc = this.locs[index];
        r.setBounds(loc.x,loc.y,cellsize.width ,cellsize.height );


    }

/*******************************************************************************************************
 *                size related
 *******************************************************************************************************/
    /**
     *
     * @param r
     */
    public void setBounds(Rectangle r) {
       logger.fine("");
       super.setBounds(r);
       MatrixLoggerMgr.fine(logger," "+r);
       reset();
   }
    private void reset(){
        logger.fine("");
         if(this.isSelfcomputesize() ){
            double[]   centers = this.computeCenters();
            cellcenters=centers;
         }

        double[] centers = this.getCellcenters();
        int size=0;

        if (centers.length <=0){
            return;

            // size=(int) (Math.abs(centers[1]-centers[0]));
        }
        else if(centers.length==1){
            size=20;
        }
        else {

            size=(int) (Math.abs(centers[1]-centers[0]));//-this.getGridsize()/2
        }
        int x,y;
        locs=new Point[centers.length ] ;

        if(this.getOrient()==SwingConstants.HORIZONTAL){
           // width=size;
            for (int i=0;i<locs.length ;i++){
                //x= i*size;
                x=(int) (centers[i]-size/2); // 
                y=0;
                locs[i]=new Point(x,y );
            }
            cellsize.setSize(size,this.indsize);


        }
        else{
            //height=size;
            for (int i=0;i<locs.length ;i++){
                x= 0;
                //y=i*size;
                y=(int) (centers[i]-size/2);
                locs[i]=new Point(x,y );
            }
            cellsize.setSize(this.indsize,size);

        }
        //logger.info("Header cell size:"+cellsize);
        MatrixLoggerMgr.fine(logger,"Header cell size:"+cellsize.toString() );
        this.repaint(); // 

    }
     private double[] computeCenters(){
        logger.fine("");
        MatrixLoggerMgr.fine(logger,"computeCenters()");
        int numOfCell = this.getNumOfCell();
        if (numOfCell<=0)return new double[0];
        double[] centers =new double[numOfCell];
        int len;
        Dimension size = this.getSize();

        if(orient==SwingConstants.HORIZONTAL ){
            len=size.width ;//.
        }
        else{     //vertical
            len=size.height ;
        }
        if(len<=0)return new double[0];
        MatrixLoggerMgr.fine(logger,"   # of cell "+numOfCell);
        MatrixLoggerMgr.fine(logger,"     size:"+size);
        MatrixLoggerMgr.fine(logger,"     len:"+len);
        double unit=len/numOfCell;
        MatrixLoggerMgr.fine(logger,"   unit="+unit+"  centers :");
        for (int i=0;i<centers.length ;i++){
            centers[i]=(i*unit)+unit/2;
            MatrixLoggerMgr.fine(logger,"     "+centers[i]);

        }
        return centers;
    }



    public double[] getCellcenters() {

        return cellcenters;
    }

    public void setCellcenters(double[] cellcenters) {

        this.cellcenters = cellcenters;
        this.setSelfcomputesize(false);
        reset();
    }



    public String[] getCelllabels() {
        return celllabels;
    }

    public void setCelllabels(String[] celllabels) {
        if(celllabels==null||celllabels.length !=this.getNumOfCell()){
           new IllegalArgumentException(this.getClass().getName() +" is passed a celllabels[] with length != # of cells");
        }
        this.celllabels = celllabels;
        reset();
    }

    public int getOrient() {
        return orient;
    }

    public void setOrient(int orient) {
        if(this.orient !=orient){
        this.orient = orient;
        this.reset();
        }
    }

    public JLabel getCellrender() {
        return cellrender;
    }

    protected int getNumOfCell(){
        int length = 0;
        if(cellcenters==null&&this.celllabels==null){
            return 0;
        }
        else if(cellcenters!=null&&cellcenters.length >0){
             length = this.cellcenters.length;
        }
        else if(celllabels!=null&&celllabels.length >0){
            length =celllabels.length;
        }

            return length;
    }

  /*  public int getGridsize() {
      return gridsize;
  }

  public void setGridsize(int gridsize) {
      this.gridsize = gridsize;
  }*/

    /**
     *
     * @param cellrender
     */
    public void setCellrender(JLabel cellrender) {
        this.cellrender = cellrender;
    }

    public boolean isSelfcomputesize() {
        return selfcomputesize;
    }

    public void setSelfcomputesize(boolean selfcomputesize) {
        this.selfcomputesize = selfcomputesize;
    }

    /**
     *
     * @return  true, column header; false, rowheader
     */
    public boolean isColumnHeader(){
        if(this.getOrient()==SwingConstants.HORIZONTAL){
            return true;
        }
        else if(this.getOrient()==SwingConstants.VERTICAL ){
            return false;
        }
        else{
            logger.warning(" unknow orientation.");
            return false;
        }
    }
    /*******************************************************************************************************
     *                selection
     *******************************************************************************************************/
    /*******************************************************************************************************
       *                selection
       *******************************************************************************************************/
      /**
       * react on external selection
       * @param e
       */
      /*public void matrixSelectionChanged(MatrixSelectionEvent e) {
          int[] selects=null;
          int selecttype = e.getSelecttype();
          int orient = this.getOrient();
          MatrixLoggerMgr.fine(logger,"matrixSelectionChanged () got selections:--> ");
          if(orient ==SwingConstants.HORIZONTAL &&
             selecttype==MatrixSelectionEvent.SELECT_COL){
              // 
              selects=e.getSelectcols();
                MatrixLoggerMgr.fine(logger,"    select columns");
          }
          else if (orient ==SwingConstants.VERTICAL&&
                  selecttype==MatrixSelectionEvent.SELECT_ROW ){
              // 
              selects=e.getSelectrows();
              MatrixLoggerMgr.fine(logger,"   select rows");
          }
          else{
              //irrelevant events, do nothing
              return;
          }
         // int[] selects= e.getSelectarray();
          if(selects==null)return;

          if(logger.isLoggable(Level.FINE ) ){
              for (int i=0;i<selects.length ;i++){
                  System.out.println("      "+selects[i]);
              }
          }
          setSelection(selects);
          //this.selectSupport.setTotalSelection(selects);
          //this.repaint(); should trigger by matrix
      }*/
      public void setSelection(int[] select) {

            this.selection.clear();
            if(select.length ==this.celllabels.length ){
                //select all, do nothing
                return;
            }
            for (int i=0;i<select.length ;i++){
                this.selection.add(select[i]);
            }

        }

       /**************************************************
        *        register to listen my selection
        ***************************************************/


    protected   int findMouseSelection( Point current) {
        //only work on current
        int numcell = this.getNumOfCell();
        Rectangle r=new Rectangle();
        for (int i=0;i<numcell;i++){
            this.getCellBound(i,r);
            if(r.contains(current) ){
               return i;//row/col
            }
        }

        return -1;  //To change body of implemented methods use File | Settings | File Templates.
    }

    protected  void fireAdd(int select){
        if(orient==SwingConstants.HORIZONTAL ){
            selectclient.addColumn(new int[]{select});

        }
        else if (orient==SwingConstants.VERTICAL ){
              selectclient.addRow(new int[]{select});
        }
        else{
            System.err.println(this+" unable to process");
        }
    }
    protected void fireRemove(int select){
        if(orient==SwingConstants.HORIZONTAL ){
           selectclient.removeColumn(new int[]{select});
        }
        else if (orient==SwingConstants.VERTICAL ){
             selectclient.removeRow(new int[]{select});

        }
        else{
            System.err.println(this+" unable to process");
        }
    }
    protected void fireSelect(int select){
        if(selectclient==null)return;
        if(orient==SwingConstants.HORIZONTAL ){
             selectclient.selectColumn(new int[]{select});

        }
        else if (orient==SwingConstants.VERTICAL ){
            selectclient.selectRow(new int[]{select});
        }
        else{
            System.err.println(this+" unable to process");
        }
    }

    protected MouseListener getSelectSupport() {
          return new DfMatrixHeader.MySelectSupport();
    }
    class MySelectSupport extends BasicMouseTool {

        public void mouseClicked(MouseEvent e){
            super.mouseClicked(e);
            int curselect = findMouseSelection(cp);
             if(this.isAddSelection(e) ){
                 if(selection.contains(curselect)){
                     fireRemove(curselect);  //for selected row/col, if click again, --> unselect
                 }
                 else
                    fireAdd(curselect);  //select
             }
             else if (isMinusSelection(e)){
                fireRemove(curselect);//useful when u do minus selection for the first time ( nothing has been selected)
             }
             else{

                    fireSelect(curselect);
             }

        }

        /**
     * extend the method to define ur policy
     * @param e
     * @return
     */
    public boolean isAddSelection(MouseEvent e){
         if(e.isShiftDown() ){//add another selection
             logger.finer("shift key down down, do addSelect");
             return true;
        }
        return false;
    }

    public boolean isMinusSelection(MouseEvent e){
        if (e.isAltDown() ){//substract selection
             logger.finer("alt key down, do minus selection");
        return true;
        }
        return false;
    }


    }//inner class


    public void setSelectclient(SelectClient selectclient) {
        this.selectclient = selectclient;
    }



}
           